package com.panopset.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import com.panopset.Logger;
import com.panopset.compat.Strings;

/**
 * File utilities.
 * 
 * @author Karl Dinwiddie
 * 
 */
public final class Filez {

	/**
	 * 
	 * @param dir
	 *            Base directory.
	 * @param path
	 *            Path relative to base directory.
	 * @return File dir/path.
	 */
	public String combinePaths(final File dir, final String path) {
		StringWriter sw = new StringWriter();
		sw.append(getCanonicalPath(dir));
		sw.append("/");
		sw.append(path);
		return sw.toString();
	}

	/**
	 * Wrapper for java.io.File.getCanonicalPath method, to handle IOException.
	 * If an IOException is caught, the exception is logged by the log function,
	 * and the results of the Exception.getMessage function are returned.
	 * 
	 * @param f
	 *            File to get canonical path of.
	 * @return result of java.io.File.getCanonicalPath
	 */
	public String getCanonicalPath(final File f) {
		try {
			if (f == null) {
				return "";
			}
			return f.getCanonicalPath();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Determine whether or not a file is available. This method will attempt to
	 * create an empty file, along with the parent directory structure, if it
	 * doesn't exist.
	 * 
	 * @param file
	 *            File to check.
	 * @return true iff the file is available.
	 */
	public boolean isAvailable(final File file) {
		if (file.exists()) {
			return true;
		}
		File parent = file.getParentFile();
		parent.mkdirs();
		try {
			return file.createNewFile();
		} catch (IOException e) {
			Logger.log(e);
			return false;
		}
	}

	/**
	 * 
	 * @param f
	 *            List file.
	 * @return File lines.
	 */
	public List<String> loadLines(final File f) {
		List<String> rtn = new ArrayList<String>();
		try {
			

        	// TODO put in try / close when we hit Java 7.
        	
        	FileReader fr = new FileReader(f);
			BufferedReader br = new BufferedReader(fr);

					
			String s = br.readLine();
			while (s != null) {
				rtn.add(s);
				s = br.readLine();
			}
			
			br.close();
			fr.close();
		} catch (IOException ex) {
			throw new RuntimeException(ex);
		}
		return rtn;
	}

	/**
	 * Save list to file.
	 * 
	 * @param f
	 *            File.
	 * @param list
	 *            List.
	 */
	public void saveLines(final File f, final List<String> list) {
		if (list == null) {
			return;
		}
		try {

        	// TODO put in try / close when we hit Java 7.
		
		FileWriter fw = new FileWriter(f);
				BufferedWriter bw = new BufferedWriter(fw);
			for (String s : list) {
				bw.write(s);
				bw.write(Strings.getEol());
			}
			bw.flush();
			bw.close();
			fw.close();
		} catch (IOException ex) {
			throw new RuntimeException(ex);
		}
	}

    /**
     * Copy file.
     *
     * @param from
     *            From file.
     * @param to
     *            To file.
     * @return true iff successful
     */
    public boolean copyFile(final File from, final File to) {
        try {
            createParentDirectories(to);
            new StreamProcessor().copyStream(new FileInputStream(from),
                    new FileOutputStream(to));
            return true;
        } catch (Exception ex) {
            Logger.log("from: " + new Filez().getCanonicalPath(from));
            Logger.log("  to: " + new Filez().getCanonicalPath(to));
            return false;
        }
    }

	/**
	 * Delete file.
	 * 
	 * @param path
	 *            Path to file to delete.
	 */
	public void delete(final String path) {
		if (path == null) {
			return;
		}
		delete(new File(path));
	}

	/**
	 * Delete file.
	 * 
	 * @param path
	 *            Path to file to delete.
	 */
	public void delete(final File path) {
		if (path == null) {
			return;
		}
		if (!path.exists()) {
			return;
		}
		if (!path.delete()) {
			Logger.log("Failed to delete " + getCanonicalPath(path));
		}
	}
	

    /**
     * Make directories for a path.
     *
     * @param path
     *            Path to make directories for.
     */
    public void mkdirs(final File path) {
        if (path == null) {
            return;
        }
        if (path.exists()) {
            return;
        }
        if (!path.mkdirs()) {
            Logger.log("Unable to create path to "
                    + getCanonicalPath(path));
        }
    }

    /**
     * Get the parent directory, fully qualified even if the file is based on a
     * relative path.
     *
     * @param f
     *            File.
     * @return Parent directory.
     */
    public String getParentDirectory(final File f) {
        if (f.exists()) {
            File fullFile = new File(getCanonicalPath(f));
            return getCanonicalPath(fullFile.getParentFile());
        }
        return getCanonicalPath(f.getParentFile());
    }

    /**
     * Create parent directories for a file.
     *
     * @param file
     *            File to create parent directories for.
     * @throws IOException
     *             IOException.
     */
    public void createParentDirectories(final File file)
            throws IOException {
        if (!file.exists()) {
            File parentDir = file.getParentFile();
            if (parentDir == null) {
                // Check to see if the file has a relative path.
                File f2 = new File(getCanonicalPath(file));
                parentDir = f2.getParentFile();
                if (parentDir == null) {
                    throw new IOException();
                }
            }
            if (parentDir.exists()) {
                return;
            }
            mkdirs(parentDir);
        }
    }

}
